package main

import (
	"encoding/json"
	sentinel "github.com/alibaba/sentinel-golang/api"
	"github.com/alibaba/sentinel-golang/core/base"
	"github.com/alibaba/sentinel-golang/core/flow"
	"io"
	"log"
	"micro-gateway/configs"
	"micro-gateway/etcd"
	"micro-gateway/initialize"
	. "micro-gateway/middlewares"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"
)

//自建http网关服务
func main(){
	//获得配置对象
	Yaml := configs.InitConfig()
	initialize.Init(Yaml)

	//服务发现
	go etcd.Dismain()

	//initSentinel初始化
	err := sentinel.InitWithConfigFile("sentinel.yaml")
	if err != nil {
		log.Fatal(err)
	}
	_, err = flow.LoadRules([]*flow.Rule{
		{
			Resource:               "resource-name",
			Threshold:              10, // 请求的间隔控制在 1000/10=100 ms
			TokenCalculateStrategy: flow.Direct,
			ControlBehavior:        flow.Reject,
			MaxQueueingTimeMs: 500, // 最长排队等待时间500ms
		},
	})
	if err != nil {
		// 加载规则失败，进行相关处理
		log.Fatal(err)
	}

	//routes := initialize.InitConfig()
	//http.HandleFunc("/", proxyHandler) //普通方式
	http.Handle("/", Pipeline(http.HandlerFunc(proxyHandler))) //使用嵌套中间件
	http.HandleFunc("/health", healthHandler)
	http.HandleFunc("/hello", HelloServer)
	log.Println("startup service success","listening on: http://localhost:8080")
	//err := http.ListenAndServe(":8080",nil)
	//if err != nil {
	//	log.Fatalf("startup service failed, err:%v\n", err)
	//}

	s := &http.Server{
		Addr:           ":8080",
		Handler:        nil,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	err = s.ListenAndServe()
	if err != nil {
		log.Fatal("startup service failed, err:%v\n", err)
	}
}

//Proxy 代理Handler
func proxyHandler(writer http.ResponseWriter, request *http.Request) {
	//解决跨域
	if origin := request.Header.Get("Origin"); origin != "" {
		writer.Header().Set("Access-Control-Allow-Origin", origin)
		writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		writer.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length," +
			" Accept-Encoding, X-CSRF-Token, Authorization, Token,client_id")
	}
	if request.Method == "OPTIONS" {
		writer.WriteHeader(204)
		return
	}
	//解决跨域end

	//效验sentinel
	// Entry 方法用于埋点
	e, b := sentinel.Entry("resource-name", sentinel.WithTrafficType(base.Inbound))
	if b != nil {
		// 请求被流控，可以从 BlockError 中获取限流详情
		// block 后不需要进行 Exit()
		writer.WriteHeader(http.StatusBadRequest) //400
		log.Println("service bad request limit")
		io.WriteString(writer, "err:limit has bean reached!\n")
		return
	} else {
		// 请求可以通过，在此处编写您的业务逻辑
		// 务必保证业务逻辑结束后 Exit
		e.Exit()
	}
	//效验sentinel end

	routes := initialize.InitConfig()
	log.Println("网关请求URL：",request.Host+request.URL.Path)
	if r := routes.Match(request);r != nil{
		remote,_ := url.Parse(r.Url)//反向代理9000端口的内网http服务
		log.Println("反向代理请求URL：",remote.Host+request.URL.Path)
		//访问http://localhost:8090/v1/test代理访问=>http://localhost:9000/servename/v1/test
		//最后去掉servename服务名，成功访问http://localhost:9000/v1/test
		urlFirst := request.URL.Path[1:] //去掉第一个斜杠“/”
		comma := strings.Index(urlFirst, "/") //comma的意思是从字符串urlFirst查找第一个斜杠“/”,然后返回他的位置
		serveName := urlFirst[0:comma]
		oldPath := "/"+ serveName
		request.URL.Path = strings.Replace(request.URL.Path, oldPath,"",-1)
		log.Println("反向代理处理请求URL：",remote.Host+request.URL.Path)
		p := httputil.NewSingleHostReverseProxy(remote)
		p.ServeHTTP(writer,request)
	}else{
		writer.WriteHeader(http.StatusNotFound) //代理路由未匹配
	}
}

func healthHandler(writer http.ResponseWriter, request *http.Request){
	//效验sentinel
	// Entry 方法用于埋点
	e, b := sentinel.Entry("resource-name", sentinel.WithTrafficType(base.Inbound))
	if b != nil {
		// 请求被流控，可以从 BlockError 中获取限流详情
		// block 后不需要进行 Exit()
		writer.WriteHeader(http.StatusBadRequest) //400
		log.Println("service bad request limit")
		io.WriteString(writer, "err:limit has bean reached!\n")
		return
	} else {
		// 请求可以通过，在此处编写您的业务逻辑
		// 务必保证业务逻辑结束后 Exit
		e.Exit()
	}
	//效验sentinel end

	log.Println("service is health")
	io.WriteString(writer, "service is health!\n")

}



//HTTP服务器返回json数据
type Data struct{
	Name string
	Age int
}
type Ret struct{
	Code int
	Param string
	Msg string
	Data []Data
}

//请求/hello?id=100
func HelloServer(w http.ResponseWriter, req *http.Request) {
	data := Data{Name: "why", Age: 18}

	ret := new(Ret)
	id := req.FormValue("id")
	//id := req.PostFormValue('id')

	ret.Code = 0
	ret.Param = id
	ret.Msg = "success"
	ret.Data = append(ret.Data, data)
	ret.Data = append(ret.Data, data)
	ret.Data = append(ret.Data, data)
	ret_json, _ := json.Marshal(ret)

	io.WriteString(w, string(ret_json))

}
